﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using System.Xml;
using System.Globalization;
using System.IO;

namespace HeapModel
{
	/// <summary>
	/// Support class for building up conditional expressions we want to include in the dgml graph
	/// </summary>
	internal static class DgmlExtensions
	{
		internal static String FormatByteTotal(int totalbytes)
		{
			Contract.Requires(totalbytes >= 0);
			if(totalbytes < 1000000)
				return String.Format("{0:0.00}", (((float)totalbytes) / 1000.0)) + "Kb";
			else
				return String.Format("{0:0.00}", (((float)totalbytes) / 1000000.0)) + "Mb";
		}

		public const string NamespaceUri = "http://schemas.microsoft.com/vs/2009/dgml";

		const String CATEGORY_CONTAINS = "\t<Category Id=\"Contains\" Label=\"Contains\" CanBeDataDriven=\"False\" CanLinkedNodesBeDataDriven=\"True\" IncomingActionLabel=\"Contained By\" IsContainment=\"True\" OutgoingActionLabel=\"Contains\" />\n";
		const String CATEGORY_ONLYREACHFROMSTATIC = "\t<Category Id=\"OnlyReachFromStatic\" Label=\"OnlyReachFromStatic\" Description=\"Boolean flag true if only reachable from a static root.\" DataType=\"System.String\" />\n";

		const String PROPERTY_INTERFERE = "\t<Property Id=\"InterferPtr\" Description=\"Boolean flag if edge represents Interfering pointers.\" DataType=\"System.String\" />\n";

		const String PROPERTY_TYPES = "\t<Property Id=\"Types\" Description=\"Types of the objects represented by this region.\" DataType=\"System.String\" />\n";
		const String PROPERTY_OFFSETS = "\t<Property Id=\"Offsets\" Description=\"Abstract storage locations associated with pointers represented by this edge.\" DataType=\"System.String\" />\n";

		const String PROPERTY_OBJCOUNT = "\t<Property Id=\"ObjectCount\" Description=\"Number of objects represented by region.\" DataType=\"System.String\" />\n";
		const String PROPERTY_SIZEINBYTES = "\t<Property Id=\"SizeInBytes\" Description=\"Number of bytes used by objects represented by node.\" DataType=\"System.String\" />\n";
		const String PROPERTY_SIZEHEAPPERCENT = "\t<Property Id=\"HeapPercent\" Description=\"Percentage of heap used by the objects in this node.\" DataType=\"System.String\" />\n";

		const String PROPERTY_LOW_DATARATIO = "\t<Property Id=\"DataRatio\" Description=\"Ratio of Data to Overhead in objects.\" DataType=\"System.String\" />\n";
		
		internal static void WriteCategoriesAndProperties(XmlWriter xw)
		{
			Contract.Requires(xw != null);

			var sb = new StringBuilder();
			sb.Append("<Categories>\n");
			sb.AppendLine();
			sb.Append(CATEGORY_CONTAINS);
			sb.Append(CATEGORY_ONLYREACHFROMSTATIC);
			sb.AppendLine();
			sb.Append("</Categories>\n");

			sb.Append("<Properties>\n");
			sb.AppendLine();
			sb.Append(PROPERTY_INTERFERE);
			sb.Append(PROPERTY_TYPES);
			sb.Append(PROPERTY_OFFSETS);
			sb.Append(PROPERTY_OBJCOUNT);
			sb.Append(PROPERTY_SIZEINBYTES);
			sb.Append(PROPERTY_SIZEHEAPPERCENT);
			sb.Append(PROPERTY_LOW_DATARATIO);
			sb.AppendLine();
			sb.Append("</Properties>\n");

			xw.WriteRaw(sb.ToString());
		}

		const string FlatStyle = "\t<Style TargetType=\"Node\"><Setter Property=\"Style\" Value=\"Plain\" /><Setter Property=\"NodeRadius\" Value=\"2\" /></Style>\n";
		const String CONDITION_HIDE_STATIC_EDGES = "\t<Style TargetType=\"Link\" IsEnabled=\"false\" GroupLabel=\"LocalReachOnly\" ValueLabel=\"1\">\n\t\t<Condition Expression=\"OnlyReachFromStatic\" />\n\t\t<Setter Property=\"Visibility\" Value=\"Hidden\" />\n\t</Style>\n";
		const String CONDITION_HIDE_STATIC_NODES = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"LocalReachOnly\" ValueLabel=\"2\">\n\t\t<Condition Expression=\"OnlyReachFromStatic\" />\n\t\t<Setter Property=\"Visibility\" Value=\"Hidden\" />\n\t</Style>\n";

		const String CONDITION_HIDE_NULL_EDGES = "\t<Style TargetType=\"Link\" IsEnabled=\"false\" GroupLabel=\"HideNullity\" ValueLabel=\"1\">\n\t\t<Condition Expression=\"NullEdge\" />\n\t\t<Setter Property=\"Visibility\" Value=\"Hidden\" />\n\t</Style>\n";
		const String CONDITION_HIDE_NULL_NODES = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"HideNullity\" ValueLabel=\"2\">\n\t\t<Condition Expression=\"NullNode\" />\n\t\t<Setter Property=\"Visibility\" Value=\"Hidden\" />\n\t</Style>\n";

		const String CONDITION_EXPAND_BACKGROUND = "\t<Style TargetType=\"Node\">\n\t\t<Condition Expression=\"Group = 'Expanded' and HasCategory('Summary')\" />\n\t\t<Setter Property=\"Background\" Value=\"GhostWhite\" />\n\t</Style>\n";
		const String CONDITION_COLLAPSED_BACKGROUND = "\t<Style TargetType=\"Node\">\n\t\t<Condition Expression=\"Group = 'Collapsed'\" />\n\t\t<Setter Property=\"Background\" Value=\"Silver\" />\n\t</Style>\n";
		const String CONDITION_ATOM_BG_OK = "\t<Style TargetType=\"Node\">\n\t\t<Condition Expression=\"HasCategory('Atom')\" />\n\t\t<Setter Property=\"Background\" Value=\"Silver\" />\n\t</Style>\n";
		const String CONDITION_LOCAL_BG_OK = "\t<Style TargetType=\"Node\">\n\t\t<Condition Expression=\"HasCategory('LocalRoot')\" />\n\t\t<Setter Property=\"Background\" Value=\"Gold\" />\n\t</Style>\n";
		const String CONDITION_STATIC_BG_OK = "\t<Style TargetType=\"Node\">\n\t\t<Condition Expression=\"HasCategory('StaticRoot')\" />\n\t\t<Setter Property=\"Background\" Value=\"Turquoise\" />\n\t</Style>\n";

		const String CONDITION_MEMORY_HEAT_MAP1 = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"MemUseHeatMap\" ValueLabel=\"1\">\n\t\t<Condition Expression=\"HeapPercent > 5\" />\n\t\t<Setter Property=\"Background\" Value=\"Purple\" />\n\t</Style>";
		const String CONDITION_MEMORY_HEAT_MAP4 = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"MemUseHeatMap\" ValueLabel=\"4\">\n\t\t<Condition Expression=\"HeapPercent > 15\" />\n\t\t<Setter Property=\"Background\" Value=\"Red\" />\n\t</Style>";

		const String CONDITION_LOW_DATARATIO1 = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"LowDataRatio\" ValueLabel=\"1\">\n\t\t<Condition Expression=\"0 &lt;= DataRatio and DataRatio &lt;= 1.0\" />\n\t\t<Setter Property=\"Background\" Value=\"Purple\" />\n\t</Style>";
		const String CONDITION_LOW_DATARATIO2 = "\t<Style TargetType=\"Node\" IsEnabled=\"false\" GroupLabel=\"LowDataRatio\" ValueLabel=\"2\">\n\t\t<Condition Expression=\"0 &lt;= DataRatio and DataRatio &lt;= 0.5\" />\n\t\t<Setter Property=\"Background\" Value=\"Red\" />\n\t</Style>";

		const String CONDITION_SHOW_INTERFERE = "\t<Style TargetType=\"Link\" IsEnabled=\"false\" GroupLabel=\"Interfere\" ValueLabel=\"1\">\n\t\t<Condition Expression=\"InterferPtr\" />\n\t\t<Setter Property=\"Stroke\" Value=\"Orange\" />\n\t\t<Setter Property=\"Background\" Value=\"Orange\" />\n\t</Style>";

		internal static void WriteStyles(XmlWriter xw)
		{
			var sb = new StringBuilder();
			sb.Append("<Styles>\n");
			sb.Append(FlatStyle);
			sb.Append(CONDITION_HIDE_STATIC_EDGES);
			sb.Append(CONDITION_HIDE_STATIC_NODES);

			sb.Append(CONDITION_HIDE_NULL_EDGES);
			sb.Append(CONDITION_HIDE_NULL_NODES);

			sb.Append(CONDITION_LOCAL_BG_OK);
			sb.Append(CONDITION_STATIC_BG_OK);

			sb.Append(CONDITION_EXPAND_BACKGROUND);

			sb.Append(CONDITION_MEMORY_HEAT_MAP4);
			sb.Append(CONDITION_MEMORY_HEAT_MAP1);

			sb.Append(CONDITION_LOW_DATARATIO2);
			sb.Append(CONDITION_LOW_DATARATIO1);
	
			sb.Append(CONDITION_SHOW_INTERFERE);

			sb.Append(CONDITION_ATOM_BG_OK);
			sb.Append(CONDITION_COLLAPSED_BACKGROUND);

			sb.Append("</Styles>\n");
			xw.WriteRaw(sb.ToString());
		}
	}
}
